#include "TeamAI.h"
#include "People.h"
#include "Team.h"
#include "ServiceProvider.h"
#include "AuctionManager.h"
#include "PeopleManager.h"
#include "PeopleFunctors.h"
#include "Utilities.h"
#include "RandomSystem.h"

#include <cassert>
#include <algorithm>

namespace
{
	struct IsGoodPlayerChoice
	{
		IsGoodPlayerChoice (int i_role, int i_money)
			:m_role(i_role)
			,m_money(i_money)
		{
		}

		bool operator()(const People* const i_player) const
		{
			return (i_player->GetSkills().m_playerSkills.m_bestRole == m_role  && (m_money > i_player->BuyValue()));
		}

		int	m_role;
		int	m_money;
	};

	struct FormationPlayer
	{
		PeopleSkills skills;
		People* player;
	};
	typedef STL::vector<FormationPlayer> FormationPlayers;
	typedef STL::vector<FormationPlayer*> FormationPlayersP;

	struct CompareByFormationSkill
	{
		explicit CompareByFormationSkill(const int i_skillNum)
			:m_skillNum(i_skillNum)
		{
		}
		bool operator()(const FormationPlayer* const i_first, const FormationPlayer* const i_second)
		{
			return i_first->skills.m_playerSkills[m_skillNum] > i_second->skills.m_playerSkills[m_skillNum];
		}
		int m_skillNum;
	};
}

TeamAI::TeamAI(ServiceProvider* const i_sp, Team* const i_team)
	:m_sp(i_sp)
	,m_team(i_team)
{
	assert(m_sp && m_team);
	m_map.resize(NUM_SKILL_ROLES);
}

void TeamAI::TryBuy(const bool i_doNotPrompt)
{
	const PeopleVec& thePlayers = m_team->GetPlayersList();
	m_map[Goalkeeper].clear();
	m_map[Defender].clear();
	m_map[Attacker].clear();
	for(PeopleVecCit pit= thePlayers.begin(); pit!= thePlayers.end(); ++pit)
		m_map[(*pit)->GetSkills().m_playerSkills.m_bestRole].push_back(*pit);

	const PeopleVec& market = m_sp->GetPeopleMgr().GetFreeVec(true); //it is sorted!

	const int teamBudget = m_team->GetBudget();
	if (teamBudget > 0)
	{
		static const TeamParams& params = Team::GetParams();
		static const int titolarPlayersPerRole[NUM_SKILL_ROLES] = {1,2,2};
		float ratioPlayersPerRole[NUM_SKILL_ROLES];

		int i = 0;
		for (PlayerRoleMapCit it = m_map.begin(); it != m_map.end(); ++it, ++i)
		{
			ratioPlayersPerRole[i] = static_cast<float>(it->size()) / static_cast<float>(titolarPlayersPerRole[i]);
		
			if(ratioPlayersPerRole[i] < 1.6f)
			{
				const int playerBudget = static_cast<int>(teamBudget * RandFloatBetween(0.25f, 0.75f));
				PeopleVecCit found = STL::find_if(market.begin(), market.end(), IsGoodPlayerChoice(i, playerBudget));
				if(found != market.end())
				{
#ifdef DARIO
					const size_t money = static_cast<int>((*found)->BuyValue() * RandFloatBetween(1.00f, 1.15f));
					m_sp->GetAuctionMgr().AddBet(Bet(m_team, *found, money));
#else
					const int salaryExpectedPlayer = static_cast<int>((*found)->CalculateExpectedSalary());

					//20% of salary that player aspected
					const int randPortionSalary = RandIntBetween(-((salaryExpectedPlayer*20)/100), ((salaryExpectedPlayer*20)/100));
					const unsigned int offer = STL::max(salaryExpectedPlayer + randPortionSalary, 1);
					const unsigned int years = static_cast<unsigned int>(RandIntBetween(1, 5));
					m_sp->GetAuctionMgr().AddBet(Bet(m_team, *found, offer, years));
#endif
				}
			}
		}
	}
	else
	{
#ifdef DARIO
		if(thePlayers.size() == Team::GetParams().MIN_TEAM_PLAYERS)
		{
			const int marmagliaIndex = RandIntBetween(market.size()-market.size()/5, market.size()-1);
			People* const marmagliaPlayer = market[marmagliaIndex];
			const int salary = marmagliaPlayer->GetSalary();
			People* mostExpensivePlayer = *(STL::min_element(thePlayers.begin(), thePlayers.end(), PeopleComparisonSalary));
			if(salary < mostExpensivePlayer->GetSalary())
			{
				const size_t money = static_cast<int>(marmagliaPlayer->BuyValue() * RandFloatBetween(1.00f, 1.05f));
				m_sp->GetAuctionMgr().AddBet(Bet(m_team, marmagliaPlayer, money));
			}
		}
#endif
	}
	TryCheat(i_doNotPrompt);
}

int TeamAI::TryCheat(const bool /*i_doNotPrompt*/)
{
	int num=0;
	while (m_team->GetPlayersList().size() + num < Team::GetParams().MIN_TEAM_PLAYERS)
	{
		size_t min = 99;
		int missingRole = Goalkeeper;
		int currRole = Goalkeeper;
		for (PlayerRoleMapCit it = m_map.begin(); it != m_map.end(); ++it, ++currRole)
		{
			const size_t i = it->size();
			if (i < min) 
			{
				min= i; 
				missingRole= currRole;
			}
		}

		People* newPlayer = m_sp->GetPeopleMgr().CreateNewPeople(false, true, true);
		NOTDARIONLY(newPlayer->SetContract(newPlayer->CalculateExpectedSalary(), Rand()%5+1));
		m_sp->GetPeopleMgr().ChangePeopleRole(newPlayer, missingRole);
		newPlayer->SetTeamID(m_team->GetID());
		m_map[missingRole].push_back(newPlayer);
		++num;
	}

	return num;
}

void TeamAI::TrySell(const bool i_doNotPrompt)
{
	//TODO check if player just has a contract
	static const float SALARY_FACTOR = 3.0f;
	static const TeamParams& params = Team::GetParams();
	static const size_t maximumSubstitutePlayersPerRole[NUM_SKILL_ROLES] = {3,6,6};

	const PeopleVec& thePlayers = m_team->GetPlayersList();
	if(thePlayers.size() <= params.MIN_TEAM_PLAYERS)
		return;

	m_map[Goalkeeper].clear();
	m_map[Defender].clear();
	m_map[Attacker].clear();
	for(PeopleVecCit pit= thePlayers.begin(); pit!= thePlayers.end(); ++pit)
		m_map[(*pit)->GetSkills().m_playerSkills.m_bestRole].push_back(*pit);

	People* pToSell = 0;

	if(m_team->GetBudget() < 0) //desperate need money
	{
		pToSell = *(STL::min_element(thePlayers.begin(), thePlayers.end(), PeopleComparisonSalary));
	}
	else
	{
		int currRole = Goalkeeper;
		for(PlayerRoleMapIt it = m_map.begin(); it != m_map.end(); ++it, ++currRole)
		{
			if(it->size() > (maximumSubstitutePlayersPerRole[currRole])) // check if too much ppl in one role
			{
				if(m_team->GetBudget() > SALARY_FACTOR * m_team->CalculateTotalSalary() ) //we dont need money
					pToSell = it->back();
				else
					pToSell = it->at(it->size()/2); //we need money and we sell someone that we pay more.
			}
		}
	}

	if(pToSell && m_team->SellPlayer(pToSell))
		if(!i_doNotPrompt)
			STD::cout << " ! " << m_team->GetName() << " ha venduto " << pToSell->GetName() << " per " << pToSell->SellValue() << " Banane!.\n";
}

void TeamAI::MakeFormation()
{
	//players

	static TeamParams teamParams = Team::GetParams();
	static FormationPlayers formationPlayers(teamParams.MAX_TEAM_PLAYERS);
	static FormationPlayersP formationPlayersP(teamParams.MAX_TEAM_PLAYERS);

	PeopleVec& thePlayers = m_team->m_players;
	formationPlayersP.resize(thePlayers.size());
	for(size_t i=0; i< formationPlayersP.size(); ++i)
	{
		FormationPlayer& theFPlayer = formationPlayers[i];
		formationPlayersP[i] = &theFPlayer;
		theFPlayer.player = thePlayers[i];
		const People& thePlayer = *theFPlayer.player;
		const PeopleSkills& skills = thePlayer.GetSkills();
		const int energyPercent = 50 + (skills.m_physicsSkills[Stamina] * 50 / skills.m_physicsSkills[Resistance]);
		const int brokenFactor = (thePlayer.GetAvailability().IsBroken() ? -1000 : 0);
		const int resFactor = skills.m_physicsSkills[Resistance]/10;
		const int ExpulseFactor = (thePlayer.GetAvailability().IsExpulse() ? -10000 : 0);
		const int factors = brokenFactor + resFactor + ExpulseFactor;
		for(int j=0; j<NUM_SKILL_ROLES; ++j)
			theFPlayer.skills.m_playerSkills[j] = (skills.m_playerSkills[j]*energyPercent/100) + factors;
		theFPlayer.skills.m_playerSkills.m_bestRole = skills.m_playerSkills.m_bestRole;
	}
	typedef FormationPlayersP::iterator FPIt;
	FPIt startGK = formationPlayersP.begin();
	FPIt startDef = startGK + 1;
	FPIt startAtt = startDef + 2;
	FPIt startNoobs = startAtt + 2;
	FPIt end = formationPlayersP.end();
	STD::nth_element(startGK, startDef, end, CompareByFormationSkill(Goalkeeper));
	STD::nth_element(startDef, startAtt, end, CompareByFormationSkill(Defender));
	STD::nth_element(startAtt, startNoobs, end, CompareByFormationSkill(Attacker));

	for(size_t i=0; i< formationPlayersP.size(); ++i)
		thePlayers[i] = formationPlayersP[i]->player;

	//trainers

	//TODO: dumb AI at the moment: random roles
	for(size_t i=0; i<Team::NUM_TRAINERS_ROLES; ++i)
		m_team->SetTrainerRole(RandIntBetween(0, m_team->GetTrainersList().size()-1), i);
}

void TeamAI::CheckFormation()
{
	PeopleVec& thePlayers = m_team->m_players;
	STD::stable_partition(thePlayers.begin(), thePlayers.end(), IsNotBroken);
	STD::stable_partition(thePlayers.begin(), thePlayers.end(), IsNotExpulse);

	for(size_t i=0; i<Team::NUM_TRAINERS_ROLES; ++i)
		if(m_team->GetTrainersRoleList()[i] == 0)
			m_team->SetTrainerRole(Rand()%m_team->GetNumTrainers(), i);
}
